﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;

namespace CommonEngine
{
    public enum ToggleCaseOption
    {
        None,
        ToUpper,
        ToLower
    }

    public enum TrimOption
    {
        None,
        TrimStart,
        TrimEnd,
        TrimAll
    }

    public static class ToolsExtend
    {
        /// <summary>
        /// 获取属性的特性描述
        /// </summary>
        public static string GetGenericTypeDescription<T>(this string Member)
        {
            var description = Member;
            if (string.IsNullOrEmpty(Member))
                return description;

            Type GenericType = typeof(T);
            MemberInfo[] memInfo = GenericType.GetMember(Member);
            if (memInfo != null && memInfo.Length > 0)
            {
                object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
                if (attrs != null && attrs.Length > 0)
                    description = ((DescriptionAttribute)attrs[0]).Description;
            }
            return description;
        }

        #region--集合工具--

        public static void ForEachSide<T>(this IEnumerable<T> source, Action<T> func)
        {
            if (source == null)
                return;

            foreach (var element in source)
            {
                if (func != null)
                    func(element);
            }
        }

        /// <summary>
        /// 分段执行相关数据操作(碎片化执行）
        /// </summary>
        public static void RunAsFragment<T>(this IEnumerable<T> source, Action<IEnumerable<T>> fragAct, int rangSize = 50)
        {
            if (source == null)
                return;

            int page = 1;
            int recordCount = source.Count();
            int totalPage = (recordCount / rangSize) + (recordCount % rangSize == 0 ? 0 : 1);
            while (page <= totalPage)
            {
                var rngData = source.Skip((page - 1) * rangSize).Take(rangSize);
                fragAct(rngData);
                page++;
            }
        }
        /// <summary>
        /// 树型数据结构递归执行
        /// </summary>
        public static void TreeRecurrde<T>(this T source, Action<T> func, Func<T, IEnumerable<T>> recursionSelecotr)
        {
            func(source);

            IEnumerable<T> recursion = recursionSelecotr(source);

            if (recursion != null)
            {
                foreach (T t in recursion)
                {
                    t.TreeRecurrde(func, recursionSelecotr);
                }
            }
        }
        /// <summary>
        /// 树型数据结构递归积累
        /// </summary>
        public static TAccumulate TreeAccumulate<TSource, TAccumulate, TResult>(this TSource source,
            TAccumulate seed,
            Func<TSource, TResult> resultSelector,
            Func<TAccumulate, TResult, TAccumulate> accumulate,
            Func<TSource, IEnumerable<TSource>> recursionSelecotr)
        {
            TResult result = resultSelector(source);

            TAccumulate accumulateResult = accumulate(seed, result);

            IEnumerable<TSource> recursion = recursionSelecotr(source);

            if (recursion != null)
            {
                foreach (TSource t in recursion)
                {
                    t.TreeAccumulate(accumulateResult, resultSelector, accumulate, recursionSelecotr);
                }
            }
            return accumulateResult;
        }

        #endregion

        #region--集合修整--

        /// <summary>
        /// 修整集合，去掉元素两边空白，元素转换大小写
        /// </summary>
        public static void TrimElements(this IList<string> list, TrimOption trimOption = TrimOption.TrimAll, ToggleCaseOption caseOption = ToggleCaseOption.None)
        {
            if (list == null)
                return;

            var result = list
                .Where(e => !string.IsNullOrWhiteSpace(e))
                .Select(e =>
                {
                    switch (trimOption)
                    {
                        case TrimOption.TrimAll:
                            e = e.Trim();
                            break;
                        case TrimOption.TrimStart:
                            e = e.TrimStart();
                            break;
                        case TrimOption.TrimEnd:
                            e = e.TrimEnd();
                            break;
                        default:
                            break;
                    }

                    switch (caseOption)
                    {
                        case ToggleCaseOption.ToUpper:
                            e = e.ToUpper();
                            break;
                        case ToggleCaseOption.ToLower:
                            e = e.ToLower();
                            break;
                        default:
                            break;
                    }
                    return e;
                }).ToList();
            list.Clear();
            result.ForEach(e => list.Add(e));
        }

        /// <summary>
        /// 修整集合，去掉空元素
        /// </summary>
        public static void TrimNullElements<T>(this IList<T> list) where T : class
        {
            if (list == null)
                return;

            var result = list.Where(e => e != null).ToList();
            list.Clear();
            result.ForEach(e => list.Add(e));
        }

        /// <summary>
        /// 修整集合，取满足条件的元素
        /// </summary>
        public static void TrimElements<T>(this IList<T> list, Func<T, bool> elementSelector) where T : class
        {
            if (list == null)
                return;

            var result = list.Where(e => elementSelector(e)).ToList();
            list.Clear();
            result.ForEach(e => list.Add(e));
        }

        /// <summary>
        /// 修整集合，去掉空白，转换大小写
        /// </summary>
        public static string TrimWhiteSpace(this string source, ToggleCaseOption caseOption = ToggleCaseOption.None)
        {
            return source == null ? null : source.Trim().ToggleCase(caseOption);
        }

        public static string TrimEndWhiteSpace(this string source, ToggleCaseOption caseOption = ToggleCaseOption.None)
        {
            return source == null ? null : source.TrimEnd().ToggleCase(caseOption);
        }

        public static string TrimStartWhiteSpace(this string source, ToggleCaseOption caseOption = ToggleCaseOption.None)
        {
            return source == null ? null : source.TrimStart().ToggleCase(caseOption);
        }

        public static string ToggleCase(this string source, ToggleCaseOption caseOption)
        {
            string result = source;

            if (source != null)
            {
                switch (caseOption)
                {
                    case ToggleCaseOption.ToLower:
                        result = result.ToLower();
                        break;
                    case ToggleCaseOption.ToUpper:
                        result = result.ToUpper();
                        break;
                    default:
                        break;
                }
            }
            return result;
        }

        #endregion

        #region--集合应用--

        /// <summary>
        /// 排除重复的元素
        /// </summary>
        public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
        {
            HashSet<TKey> seenKeys = new HashSet<TKey>();
            foreach (TSource element in source)
            {
                //---HashSet---同样的Key不接受添加两次
                if (seenKeys.Add(keySelector(element)))
                {
                    yield return element;
                }
            }
        }

        /// <summary>
        /// 根据指定条件返回集合中不重复的元素
        /// </summary>
        public static IQueryable<T> DistinctBy<T, TKey>(this IQueryable<T> source, Expression<Func<T, TKey>> keySelector)
        {
            return source.GroupBy(keySelector).Select(group => group.FirstOrDefault());
        }

        /// <summary>
        /// 根据指定条件返回集合中不重复的元素
        /// </summary>
        /// <param name="source"></param>
        /// <param name="keySelector">键选择器</param>
        /// <param name="predicate">默认匹配条件</param>
        /// <returns></returns>
        public static IQueryable<T> DistinctBy<T, TKey>(this IQueryable<T> source, Expression<Func<T, TKey>> keySelector, Func<T, bool> predicate)
        {
            return source.GroupBy(keySelector).Select(group => group.FirstOrDefault(predicate));
        }

        #endregion

        #region---Delegate Ext---

        /// <summary>
        /// 出现不预期异常，重复多次执行
        /// </summary>
        /// <param name="action">执行的方法</param>
        /// <param name="limitTryTimes">尝试次数</param>
        /// <param name="actIfException">异常后执行方法(传入重试次数)</param>
        /// <param name="getDelay">两次重试间隔时长(传入异常，重试次数)，为null则间隔3秒。返回值单位：毫秒</param>
        /// <param name="filterRetryException">过滤哪些异常需要重试，为null则所有异常都重试。</param>
        public static void MultipleTimesWhileException(this Action action,
            int limitTryTimes,
            Action<Exception, int> actIfException = null,
            Func<Exception, int, int> getDelay = null,
            Func<Exception, bool> filterRetryException = null)
        {
            if (action == null)
                return;

            if (getDelay == null)
                getDelay = (ex, times) => 3000;

            int tryTimes = 0;
            bool failed = false;

            do
            {
                tryTimes++;
                try
                {
                    action();
                    failed = false;
                }
                catch (Exception ex)
                {
                    failed = true;

                    if (actIfException != null)
                        actIfException(ex, tryTimes);

                    if (tryTimes == limitTryTimes || (filterRetryException != null && !filterRetryException(ex)))
                    {
                        throw;
                    }

                    int delay = getDelay(ex, tryTimes);
                    System.Threading.Thread.Sleep(delay);
                }

            } while (failed && tryTimes < limitTryTimes);
        }

        /// <summary>
        /// 执行失败时，重复多次执行
        /// </summary>
        /// <param name="func">执行的方法，返回执行结果</param>
        /// <param name="judgeSuccess">是否执行成功(传入执行结果)</param>
        /// <param name="limitTryTimes">尝试次数(1开始)，1表示失败不重试</param>
        /// <param name="actIfSuccess">成功后执行方法(传入重试次数)</param>
        /// <param name="actAdjustIfFailure">失败后调整(传入重试次数)</param>
        /// <param name="actIfFinallyFailure">最终失败后执行方法(传入重试次数)</param>
        /// <param name="getDelay">两次重试间隔时长(传入重试次数)，为null则间隔3秒。返回值单位：毫秒</param>
        public static void MultipleTimesWhileFailure<T>(this Func<T> func,
            Func<T, bool> judgeSuccess,
            int limitTryTimes,
            Action<T, int> actIfSuccess = null,
            Action<T, int> actAdjustIfFailure = null,
            Action<T, int> actIfFinallyFailure = null,
            Func<int, int> getDelay = null)
        {
            if (func == null || judgeSuccess == null)
                return;

            if (getDelay == null)
                getDelay = (times) => 3000;

            int tryTimes = 0;
            bool failed = false;
            do
            {
                var result = func();
                failed = !judgeSuccess(result);
                tryTimes++;
                if (!failed)
                {
                    if (actIfSuccess != null)
                        actIfSuccess(result, tryTimes);
                }
                else
                {
                    if (tryTimes < limitTryTimes)
                    {
                        if (actAdjustIfFailure != null)
                            actAdjustIfFailure(result, tryTimes);
                    }
                    else
                    {
                        if (actIfFinallyFailure != null)
                            actIfFinallyFailure(result, tryTimes);
                    }

                    int delay = getDelay(tryTimes);
                    System.Threading.Thread.Sleep(delay);
                }

            } while (failed && tryTimes < limitTryTimes);
        }

        /// <summary>
        /// 分多段执行集合任务
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action">执行单元任务</param>
        /// <param name="originalData">原始数据集合(分段对象)</param>
        /// <param name="segmentSize">单个线程任务数。0表示所有任务</param>
        /// <param name="taskSize">同时并发线程数。0表示所有线程</param>
        public static void ExcuteSegmentsByMultiTasks<T>(this Action<List<T>> action, 
            List<T> originalData, 
            int segmentSize, 
            int taskSize)
        {
            if (action == null || originalData == null || originalData.Count() == 0)
                return;

            List<Task> tasks = new List<Task>();
            Action<object> act = t => { action(t as List<T>); };

            if (segmentSize <= 0)
            {
                tasks.Add(new Task(act, originalData, TaskCreationOptions.LongRunning));
            }
            else
            {
                var page = originalData.Count() / segmentSize + (originalData.Count() % segmentSize > 0 ? 1 : 0);
                for (int i = 0; i < page; i++)
                {
                    var tmpDatas = originalData.Skip(i * segmentSize).Take(segmentSize).ToList();
                    tasks.Add(new Task(act, tmpDatas, TaskCreationOptions.LongRunning));
                }
            }

            #region 并发执行线程

            if (taskSize <= 0)
            {
                tasks.ForEach(t => t.Start());
                Task.WaitAll(tasks.ToArray());
            }
            else
            {
                var taskPage = tasks.Count / taskSize + (tasks.Count % taskSize > 0 ? 1 : 0);
                for (int i = 0; i < taskPage; i++)
                {
                    var pageTasks = tasks.Skip(taskSize * i).Take(taskSize).ToArray();
                    pageTasks.ForEachSide(t => t.Start());
                    Task.WaitAll(pageTasks);
                }
            }

            #endregion
        }

        #endregion

        #region---获取描述---

        public static string GetDescription(this Enum obj)
        {
            var attribs = (DescriptionAttribute[])obj.GetType().GetField(obj.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attribs.Length > 0 ? attribs[0].Description : obj.ToString();
        }

        public static string GetDescriptionByInt(this Enum obj, int? id)
        {
            var values = from Enum e in Enum.GetValues(obj.GetType())
                         select new { id = Convert.ToInt32(e), name = e.GetDescription() };

            if (!id.HasValue)
                id = 1;

            try
            {
                return values.ToList().Find(c => c.id == id).name;
            }
            catch
            {
                return "";
            }
        }

        public static T GetEnumByInt<T>(this int value) where T : struct
        {
            var values = from Enum e in Enum.GetValues(typeof(T))
                         select new { id = Convert.ToInt32(e), name = e.ToString() };

            var dic = values.ToDictionary(a => a.id, b => b.name);
            if (dic.ContainsKey(value))
            {
                T res;
                Enum.TryParse<T>(dic[value], true, out res);
                return res;
            }
            else
            {
                return default(T);
            }
        }

        public static string GetEnumDescriptionByInt<T>(this int value) where T : struct
        {
            var values = from Enum e in Enum.GetValues(typeof(T))
                         select new { id = Convert.ToInt32(e), name = e.GetDescription() };

            var dic = values.ToDictionary(a => a.id, b => b.name);
            if (dic.ContainsKey(value))
            {
                return dic[value];
            }
            else
            {
                return string.Empty;
            }
        }

        public static Dictionary<int, string> GetEnumDescr(this Enum obj)
        {
            var values = from Enum e in Enum.GetValues(obj.GetType())
                         select new { id = Convert.ToInt32(e), name = e.GetDescription() };

            return values.ToDictionary(a => a.id, b => b.name);
        }

        public static Dictionary<string, string> GetEnumKV(this Enum obj)
        {
            var values = from Enum e in Enum.GetValues(obj.GetType())
                         select new { id = e.ToString(), name = e.GetDescription() };

            return values.ToDictionary(a => a.id, b => b.name);
        }

        #endregion
    }
}
